package com.lookme.lmtool.cracker.spliter;



import com.lookme.lmtool.cracker.annotation.ParamAnnotation;
import com.lookme.lmtool.cracker.annotation.SpliterAnnotation;
import com.lookme.lmtool.model.Rect;
import com.lookme.lmtool.model.SpliterInfo;
import com.lookme.lmtool.utils.ImageUtils;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import lombok.Getter;
import lombok.Setter;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Getter@Setter
@SpliterAnnotation("联通分割")
public class ContourSpliter  extends AbstractSpliter {

    @XStreamAsAttribute
    @ParamAnnotation(name = "最小的联通点", desc = "最小的联通点",inputType = ParamAnnotation.InputType.Spinner,min =1)

    int minPointCount=10;


    @XStreamAsAttribute
    @ParamAnnotation(name = "合并垂直联通点", desc = "合并垂直联通点",inputType = ParamAnnotation.InputType.Radio,width = 45)
    boolean mergeVertical=false;


    @XStreamAsAttribute
    @ParamAnnotation(name = "合并垂直阀值", desc = "合并垂直阀值",inputType = ParamAnnotation.InputType.Spinner,min =1,max=8)
    int threshold=2;


    @Override
    public List<SpliterInfo> split(BufferedImage img) {
        List<SpliterInfo> images = new ArrayList<>();
        // 获取图片宽高
        int width = img.getWidth();
        int height = img.getHeight();
        HashMap<Point, Integer> pointMap;// 一个图块的点（黑色点）
        Point point;
        // 用于装填每个图块的点数据
        List<HashMap<Point, Integer>> pointList = new ArrayList<HashMap<Point, Integer>>();
        // 根据宽高遍历图片中的所有点进行计算
        for (int x = 0; x < width; ++x) {
            for (int y = 0; y < height; ++y) {
                // 找到一个点
                label:
                if (ImageUtils.isBlack(img.getRGB(x, y))) {
                    point = new Point(x, y);// java.awt.Point
                    for (HashMap<Point, Integer> pointHashMap : pointList) {
                        if (pointHashMap.get(point) != null) {
                            break label;// 跳到标签处,此时不会再执行下面的内容.
                        }
                    }

                    pointMap = new HashMap<Point, Integer>();
                    // 这个用法很关键,根据Map的KEY值不能重复的特点避免重复填充point
                    pointMap.put(point, 1);
                    // 根据当前坐标点，递归获取与该点连接的所有点
                    getAllPoint(x, y, img, pointMap, 0);
                    pointList.add(pointMap);
                    break;
                }
            }
        }
        List<Rect> chunkRectList = new ArrayList<>();
        BufferedImage imgCell;
        int l, t, r, b, w, h, index;
        // 根据提取出来的point创建各个碎图块
        for (int i = 0; i < pointList.size(); ++i) {
            pointMap = pointList.get(i);
            // 图片的左,上,右,下边界以及宽,高
            l = t = r = b = w = h = index = 0;
            for (Point p : pointMap.keySet()) {
                if (index == 0) {
                    // 用第一个点来初始化碎图的四个边界
                    l = p.x;
                    t = p.y;
                    r = p.x;
                    b = p.y;
                } else {
                    // 再根据每个点与原有的点进行比较取舍四个边界的值
                    l = Math.min(l, p.x);
                    t = Math.min(t, p.y);
                    r = Math.max(r, p.x);
                    b = Math.max(b, p.y);
                }
                index++;
            }
            w = r - l + 1;
            h = b - t + 1;
            // 去除杂点(小于50像素数量的点集不要)
            if (pointMap.size() < minPointCount) {
                continue;
            }
            // 将切好的图放入上文传入的容器中(不传入容器的话这里可以用于返回)
            Rect rect = Rect.builder().x(l).y(t).width(w).height(h).pointMap(pointMap).build();
            chunkRectList.add(rect);
        }

        if (mergeVertical && chunkRectList.size() > 1) {
            for (int i = 1; i < chunkRectList.size(); i++) {
                if (sameXaxisRect(chunkRectList.get(i - 1), chunkRectList.get(i))) {
                    chunkRectList.set(i - 1, mergeRect(chunkRectList.get(i - 1), chunkRectList.get(i)));
                    chunkRectList.remove(i);
                    i--;
                }
            }
        }

        try {
            for (Rect rect : chunkRectList) {
                // 创建个图片空壳子(里面的所有点值都是0,即黑色)
                imgCell = new BufferedImage(rect.getWidth(), rect.getHeight(), BufferedImage.TYPE_INT_RGB);
                // 先将所有点替换成白色(反正我没有找到new BufferedImage的时候可以初始化像素色值的)
                for (int x = 0; x < rect.getWidth(); ++x) {
                    for (int y = 0; y < rect.getHeight(); ++y) {
                        imgCell.setRGB(x, y, 0xffffffff);// 图片换成白底
                    }
                }
                // 图像对应点换成黑色(如果上面换成白底的一步不做的话下面这里可以替换成白色,就变成了黑底白色的碎图了)
                for (Point p : rect.getPointMap().keySet()) {
                    imgCell.setRGB(p.x - rect.getX(), p.y - rect.getY(), 0);
                }

                SpliterInfo spliterInfo = SpliterInfo.builder().image(imgCell).rect(rect).build();
                images.add(spliterInfo);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        return images;

    }

    public boolean sameXaxisRect(Rect r1,Rect r2){
        //如果r2的开始X坐标在R1矩形的区域X坐标中
        if((r2.getX()+threshold>=r1.getX()||r2.getX()-threshold>=r1.getX())&&(r2.getX()+threshold<=r1.getEndX()||r2.getX()-threshold<=r1.getEndX())){
            if(r2.getEndX()-threshold<=r1.getEndX()||r2.getEndX()+threshold<=r1.getEndX()){
                return true;
            }
            if((r1.getX()+threshold>=r2.getX()||r1.getX()-threshold>=r2.getX())&&((r1.getEndX()-threshold<=r2.getEndX()||r1.getEndX()+threshold<=r2.getEndX()))){
                return true;
            }
        }

        return false;
    }


    public Rect mergeRect(Rect rect1,Rect rect2){
        int minStartX=0,minStartY=0,maxEndX=0,maxEndY=0;

        minStartX=rect1.getX()<rect2.getX()?rect1.getX():rect2.getX();
        minStartY=rect1.getY()<rect2.getY()?rect1.getY():rect2.getY();
        maxEndX=rect1.getEndX()>rect2.getEndX()?rect1.getEndX():rect2.getEndX();
        maxEndY=rect1.getEndY()>rect2.getEndY()?rect1.getEndY():rect2.getEndY();

        Rect merge=new Rect();
        merge.setX(minStartX);
        merge.setY(minStartY);
        merge.setWidth(maxEndX-minStartX);
        merge.setHeight(maxEndY-minStartY);
        rect1.getPointMap().putAll(rect2.getPointMap());
        merge.setPointMap(rect1.getPointMap());
        return merge;
    }


    /**
     * 根据当前坐标点，递归获取与该点连接的所有点
     * @param x         当前点x轴坐标
     * @param y         当前点y轴坐标
     * @param img       原图
     * @param pointMap  一个图块的点（黑色点），可能是一个字符也可能是多个字符
     * @param diguiTime 递归次数
     */
    private static void getAllPoint(int x, int y, BufferedImage img, HashMap<Point, Integer> pointMap, int diguiTime) {
        diguiTime++;// 递归次数
        if (diguiTime >= 2500) {
            throw new RuntimeException("递归次数大于2500");
        }
        // 左边
        Point pl = new Point(x - 1, y);
        if (x - 1 >= 0 && ImageUtils.isBlack(img.getRGB(x - 1, y)) && pointMap.get(pl) == null) {
            pointMap.put(pl, 1);
            getAllPoint(x - 1, y, img, pointMap, diguiTime);
        }
        // 右边
        Point pr = new Point(x + 1, y);
        if (x + 1 < img.getWidth() && ImageUtils.isBlack(img.getRGB(x + 1, y)) && pointMap.get(pr) == null) {
            pointMap.put(pr, 1);
            getAllPoint(x + 1, y, img, pointMap, diguiTime);
        }
        // 上边
        Point pt = new Point(x, y - 1);
        if (y - 1 >= 0 && ImageUtils.isBlack(img.getRGB(x, y - 1)) && pointMap.get(pt) == null) {
            pointMap.put(pt, 1);
            getAllPoint(x, y - 1, img, pointMap, diguiTime);
        }
        // 下边
        Point pb = new Point(x, y + 1);
        if (y + 1 < img.getHeight() && ImageUtils.isBlack(img.getRGB(x, y + 1)) && pointMap.get(pb) == null) {
            pointMap.put(pb, 1);
            getAllPoint(x, y + 1, img, pointMap, diguiTime);
        }
        diguiTime--;
    }



}
